/*
*Copyright 2021-2023 NERCIS
*
*Licensed under the Apache License, Version 2.0 (the "License");
*you may not use this file except in compliance with the License.
*You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*Unless required by applicable law or agreed to in writing, software
*distributed under the License is distributed on an "AS IS" BASIS,
*WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*See the License for the specific language governing permissions and
*limitations under the License.
*/

package cn.ac.nercis.pes.community.controller.evaluation.onsite;

import cn.ac.nercis.pes.common.constant.evaluation.EvaluationModule;
import cn.ac.nercis.pes.common.constant.project.FinishState;
import cn.ac.nercis.pes.community.component.controls.TreeListCell;
import cn.ac.nercis.pes.community.component.dialog.MessageDialog;
import cn.ac.nercis.pes.community.component.dialog.MessageType;
import cn.ac.nercis.pes.community.model.vo.MenuVO;
import cn.ac.nercis.pes.community.utils.AppStore;
import cn.ac.nercis.pes.community.utils.ControlUtils;
import cn.ac.nercis.pes.community.utils.LogUtils;
import cn.ac.nercis.pes.community.utils.SpringUtils;
import cn.ac.nercis.pes.community.views.evaluation.onsite.AssetContentView;
import cn.ac.nercis.pes.community.views.evaluation.onsite.AssetGroupView;
import cn.ac.nercis.pes.model.dto.common.TreeNode;
import cn.ac.nercis.pes.model.dto.evaluation.onsite.AssetEvalProcessDTO;
import cn.ac.nercis.pes.service.evaluation.EvaluationRecordService;
import cn.ac.nercis.pes.service.project.SystemProcessService;
import javafx.application.Platform;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.collections.FXCollections;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.image.ImageView;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.StackPane;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

import java.net.URL;
import java.util.List;
import java.util.Objects;
import java.util.ResourceBundle;
import java.util.concurrent.CompletableFuture;

/**
 * 现场测评管理控制器
 * @author zpy
 * @version 1.0.0
 */
@Slf4j
public class EvalController implements Initializable {

    @FXML
    private StackPane page;

    @FXML
    private BorderPane bodyPane;
    @FXML
    private AnchorPane emptyPane;
    @FXML
    private ImageView emptyImageView;
    @FXML
    private Label emptyLabel;
    @FXML
    private TreeView<MenuVO> menuTreeView;

    private final static Integer SHOW_MAX_NODE_TEXT_LENGTH = 10;
    private final static String NODE_NAME_FORMAT = "%s(%d/%d)";
    private final EvaluationRecordService evaluationRecordService;
    private final SystemProcessService processService;

    private TreeItem<MenuVO> rootMenu;
    private String currentSystemId;
    private MenuVO menuItem;

    private final BooleanProperty enablePaste = new SimpleBooleanProperty(false);


    public EvalController() {
        this.evaluationRecordService = SpringUtils.getBean(EvaluationRecordService.class);
        this.processService = SpringUtils.getBean(SystemProcessService.class);
    }

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        ControlUtils.initEmptyImageView(this.emptyImageView,log);
        rootMenu = new TreeItem<>(new MenuVO(0,"","资产分类与对象数据",""));
        this.initMenuControl();
    }

    public void initializeData(String systemId){
        LogUtils.info(log, "初始化现场测评...");
        this.currentSystemId = systemId;
        this.loadMenuData();
    }

    public StackPane getPage(){
        return this.page;
    }

    public void refreshAssetNode(String assetId) {
        LogUtils.info(log, "获取系统资产树节点信息...");
        LogUtils.record(log, "请求数据，systemId：{},assetId:{}", this.currentSystemId, assetId);
        CompletableFuture.supplyAsync(() -> evaluationRecordService.getAssetTreeNode(this.currentSystemId, assetId))
                .thenAccept(res -> Platform.runLater(() -> {
                    if (res.isSuccess()) {
                        TreeNode newNode = res.getData();
                        rootMenu.getChildren().stream()
                                .filter(ti -> ti.getChildren().size() > 0)
                                .flatMap(ti -> ti.getChildren().stream())
                                .forEach(item -> {
                                    var node = item.getValue();
                                    AssetEvalProcessDTO assetEvalProcess = (AssetEvalProcessDTO) newNode.getData();
                                    if(StringUtils.equals(node.getCode(),assetId)) {
                                        var text = ControlUtils.getTextByLimit(newNode.getName(),SHOW_MAX_NODE_TEXT_LENGTH);
                                        var showText = String.format(NODE_NAME_FORMAT, text, assetEvalProcess.getFinishCount(),
                                                assetEvalProcess.getCount());
                                        node.setTitle(showText);
                                        FinishState state = FinishState.NONE;
                                        if (assetEvalProcess.getCount() > 0) {
                                            state = assetEvalProcess.getFinishCount().equals(assetEvalProcess.getCount()) ? FinishState.FINISH : FinishState.INCOMPLETE;
                                        }
                                        node.setToolTip(newNode.getName());
                                        node.setIcon(ControlUtils.finishStateToIcon(state));
                                        node.setState(state);
                                    }
                                });
                        refreshAssetGroupState(newNode);
                        refreshAssetClassMenu(assetId);
                        menuTreeView.refresh();
                    } else {
                        MessageDialog.builder()
                                .withContainer(this.page)
                                .withType(MessageType.FAIL)
                                .withMessage(res.getMsg())
                                .build().show();
                    }
                }))
                .exceptionally((ex) -> {
                    log.error("获取系统资产树节点信息出错!", ex);
                    return null;
                });
    }

    private void refreshAssetClassMenu(String assetId) {
        var acMenu = rootMenu.getChildren().stream()
                .filter(node -> existAsset(node, assetId))
                .findFirst()
                .orElse(null);
        if (Objects.nonNull(acMenu)) {
            long finishCount = acMenu.getChildren().stream()
                    .map(TreeItem::getValue)
                    .filter(item -> item.getState() == FinishState.FINISH)
                    .count();
            FinishState state = finishCount == acMenu.getChildren().size() ? FinishState.FINISH : FinishState.INCOMPLETE;
            CompletableFuture.supplyAsync(() -> processService.setSystemProcess(this.currentSystemId,
                            EvaluationModule.fromNameByScene(acMenu.getValue().getTitle()), state))
                    .thenAccept(res -> Platform.runLater(() -> {
                        if (res.isSuccess()) {
                            acMenu.getValue().setIcon(ControlUtils.finishStateToIcon(state));
                            acMenu.getValue().setState(state);
                        }
                    }))
                    .exceptionally((ex) -> {
                        log.error("设置测评进度信息出错!", ex);
                        return null;
                    });
        }
    }

    private void refreshAssetGroupState(TreeNode node){
        if(CollectionUtils.isNotEmpty(node.getChildren())){
            var groupView = AppStore.getEvaluationView(this.currentSystemId, AssetGroupView.class);
            if(groupView.getController() instanceof  AssetGroupController controller){
                controller.refreshAssetGroupState(node.getChildren());
            }
        }
    }

    private boolean existAsset(TreeItem<MenuVO> acMenu, String assetId) {
        if (CollectionUtils.isEmpty(acMenu.getChildren())) {
            return false;
        }
        return acMenu.getChildren().stream()
                .anyMatch(item -> StringUtils.equals(item.getValue().getCode(),assetId));
    }

    private void initMenuControl() {
        menuTreeView.setCellFactory(tree -> new TreeListCell(20, true));
        menuTreeView.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
            if (Objects.nonNull(newValue) && Objects.nonNull(newValue.getValue()) && newValue.getValue().getId() == 2) {
                loadContentData(newValue.getValue());
                menuItem = newValue.getValue();
            }
        });
        menuTreeView.setRoot(rootMenu);
        menuTreeView.setShowRoot(false);
    }

    private void loadMenuData() {
        LogUtils.info(log, "获取系统资产树节点数据...");
        LogUtils.record(log, "请求数据:{}", this.currentSystemId);
        CompletableFuture.supplyAsync(() -> evaluationRecordService.findAssetTree(this.currentSystemId))
                .thenAccept(res -> Platform.runLater(() -> {
                    if (res.isSuccess()) {
                        this.bodyPane.setVisible(true);
                        this.emptyPane.setVisible(false);
                        this.initMenuData(res.getData());
                    } else {
                        this.bodyPane.setVisible(false);
                        this.emptyPane.setVisible(true);
                        this.emptyLabel.setText(res.getMsg());
                    }
                }))
                .exceptionally((ex) -> {
                    log.error("获取系统资产树节点数据出错!", ex);
                    return null;
                });
    }

    private void initMenuData(List<TreeNode> assetNodes) {
        rootMenu.getChildren().clear();
        TreeItem<MenuVO> defaultSelected = null;
        for (TreeNode ac : assetNodes) {
            if (CollectionUtils.isEmpty(ac.getChildren())) {
                continue;
            }
            var menuItem = new MenuVO(1,ac.getId(),ac.getName(),"none-process-icon");
            TreeItem<MenuVO> acMenus = new TreeItem<>(menuItem);
            int finishItemCount = 0;
            for(TreeNode asset : ac.getChildren()){
                var assetEvalProcess = (AssetEvalProcessDTO) asset.getData();
                FinishState state = FinishState.NONE;
                if (assetEvalProcess.getCount() > 0) {
                    state = assetEvalProcess.getFinishCount().equals(assetEvalProcess.getCount()) ? FinishState.FINISH : FinishState.INCOMPLETE;
                }
                if(state == FinishState.FINISH){
                    finishItemCount++;
                }

                String text = ControlUtils.getTextByLimit(asset.getName(),SHOW_MAX_NODE_TEXT_LENGTH);
                String showText = String.format(NODE_NAME_FORMAT, text, assetEvalProcess.getFinishCount(),assetEvalProcess.getCount());
                var subMenuItem = new MenuVO(2,asset.getId(),showText,ControlUtils.finishStateToIcon(state),"", asset.getName());
                subMenuItem.setState(state);
                subMenuItem.setData(ac.getName());
                if(CollectionUtils.isNotEmpty(asset.getChildren())){
                    var subAssets = FXCollections.observableList(asset.getChildren().stream()
                            .map(tn->{
                                var subAssetEvalProcess = (AssetEvalProcessDTO) tn.getData();
                                FinishState subAssetState = FinishState.NONE;
                                if (subAssetEvalProcess.getFinishCount() > 0) {
                                    subAssetState = subAssetEvalProcess.getFinishCount().equals(subAssetEvalProcess.getCount()) ? FinishState.FINISH : FinishState.INCOMPLETE;
                                }
                                var subAssetItem = new MenuVO(3,tn.getId(),tn.getName(),ControlUtils.finishStateToIcon(subAssetState),"", tn.getName());
                                subAssetItem.setState(subAssetState);
                                subAssetItem.setData(asset.getName());
                                return subAssetItem;
                            })
                            .toList());
                    subMenuItem.setChildren(subAssets);
                }
                acMenus.getChildren().add(new TreeItem<>(subMenuItem));
                if(Objects.isNull(defaultSelected)){
                    defaultSelected = acMenus.getChildren().get(0);
                }
            }
            if(ac.getChildren().size()>0){
                FinishState acState = ac.getChildren().size() == finishItemCount ? FinishState.FINISH : FinishState.INCOMPLETE;
                menuItem.setIcon(ControlUtils.finishStateToIcon(acState));
                menuItem.setState(acState);
            }
            rootMenu.getChildren().add(acMenus);
        }
        if (Objects.nonNull(defaultSelected)) {
            menuTreeView.getSelectionModel().select(defaultSelected);
        }
    }

    private void loadContentData(MenuVO menuItem){
        if(CollectionUtils.isNotEmpty(menuItem.getChildren())){
            var groupView = AppStore.getEvaluationView(this.currentSystemId, AssetGroupView.class);
            bodyPane.setCenter(Objects.requireNonNull(groupView).load());
            if(groupView.getController() instanceof  AssetGroupController controller){
                controller.initializeData(this.currentSystemId,menuItem);
            }
        }else{
            var contentView = AppStore.getEvaluationView(this.currentSystemId, AssetContentView.class);
            bodyPane.setCenter(Objects.requireNonNull(contentView).load());
            if(contentView.getController() instanceof  AssetContentController controller){
                controller.initializeData(this.currentSystemId,menuItem);
            }
        }
    }

}